#pragma once
#include <condition_variable>


template <typename T>
class BlockingQueue {
public:
	BlockingQueue() : mutex_(), cond_(), queue_(), stop_(false) {

	}

	BlockingQueue(const BlockingQueue&) = delete;
	BlockingQueue& operator=(const BlockingQueue&) = delete;

	void push(const T& x) {
		std::lock_guard<std::mutex> lock(mutex_);
		queue_.push_back(x);
		cond_.notify_one();
	}

	bool pop(T& x) {
		std::unique_lock<std::mutex> lock(mutex_);
		cond_.wait(lock, [this] { 
			return stop_ || !queue_.empty(); });
		
		if (stop_)
			return false;

		assert(!queue_.empty());
		//T front(std::move(queue_.front()));
		x = std::move(queue_.front());
		queue_.pop_front();
		//return front;
		return true;
	}

	void stop()
	{
		//std::unique_lock<std::mutex> lock(mutex_);
		stop_ = true;
		cond_.notify_all();
	}

	void start()
	{
		stop_ = false;
	}

	void prune()
	{
		std::lock_guard<std::mutex> lock(mutex_);
		queue_.clear();
	}

	size_t size() const {
		std::lock_guard<std::mutex> lock(mutex_);
		return queue_.size();
	}

private:
	mutable std::mutex mutex_;
	std::condition_variable cond_;
	std::deque<T> queue_;
	bool stop_;

};